package com.fary.context.support;

import com.fary.core.SpringException;
import com.fary.beans.factory.config.ConfigurableListableBeanFactory;
import com.fary.beans.factory.support.DefaultListableBeanFactory;
import com.fary.context.ApplicationContext;


import java.io.IOException;

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

    private Boolean allowBeanDefinitionOverriding;

    private Boolean allowCircularReferences;

    /** Bean factory for this context. */
    private DefaultListableBeanFactory beanFactory;

    /** Synchronization monitor for the internal BeanFactory. */
    private final Object beanFactoryMonitor = new Object();

    public AbstractRefreshableApplicationContext() {
    }

    public AbstractRefreshableApplicationContext(ApplicationContext parent) {
        super(parent);
    }


    /**
     * 刷新BeanFactory容器
     * @throws SpringException
     */
    @Override
    protected final void refreshBeanFactory() throws SpringException {
        // 1.销毁已经存在的BeanFactory
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 2.创建新的BeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 3.设置序列化id
            beanFactory.setSerializationId(getId());
            // 4.自定义设置BeanFactory一些属性，allowBeanDefinitionOverriding、allowCircularReferences
            customizeBeanFactory(beanFactory);
            // 5.加载BeanDefinition
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException ex) {
            throw new SpringException("I/O error parsing bean definition source for ", ex);
        }
    }

    protected final boolean hasBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            return (this.beanFactory != null);
        }
    }

    @Override
    public final ConfigurableListableBeanFactory getBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("BeanFactory not initialized or already closed - " + "call 'refresh' before accessing beans via the ApplicationContext");
            }
            return this.beanFactory;
        }
    }

    @Override
    protected void cancelRefresh(SpringException ex) {
        synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory != null) {
                this.beanFactory.setSerializationId(null);
            }
        }
        super.cancelRefresh(ex);
    }

    @Override
    protected final void closeBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory != null) {
                this.beanFactory.setSerializationId(null);
                this.beanFactory = null;
            }
        }
    }

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws SpringException, IOException;

}